<?php


namespace lib;


use think\cache\driver\Redis;

/**
 * Class LibDelayQueue
 * @package lib
 * @example 使用规则和方法
 * 创建延时队列
 * $cacheConfig = Config::get('cache');
 * $obj = new Redis($cacheConfig['redis']);
 * $queue = "test";
 * $delayQueue = LibDelayQueue::getInstance($obj, $queue);
 * $delayQueue->addTask($execTime, $data);
 */
class LibDelayQueue
{
    private static $_instance;
    private $redis;
    const IS_SUPPORT_PIPELINE = false; //是否支持 pipeline

    private $queue = ''; //延时队列名字

    public static function getInstance(Redis $redis, $queue = '')
    {
        if (!self::$_instance) {
            self::$_instance = new LibDelayQueue($redis, $queue);
        }
        return self::$_instance;
    }

    private function __construct(Redis $redis, $queue)
    {
        $this->queue = $queue;
        $this->redis = $redis;
    }

    /**
     * @param $runTime
     * @param $member
     */
    public function addTask($runTime, $member)
    {
        $this->redis->zadd($this->queue, $runTime, $member);
    }

    /**
     * @param $member
     */
    public function delDelay($member)
    {
        $this->redis->zrem($this->queue, $member);
    }

    /**
     * @param array $data [['runTime'=>00, 'member'=>'']]
     * @return mixed
     * @throws \Exception
     */
    public function batchAddTask(array $data)
    {
        if (empty($data['runTime'] || empty($data['member']))) {
            throw new \Exception('批量发送到延时队列data格式错误');
        }
        // 是否支持 pipeline
        if (self::IS_SUPPORT_PIPELINE) {
            $redisIns = $this->redis->multi(Redis::PIPELINE);
            foreach ($data as $item) {
                $redisIns->zadd($this->queue, $item['runTime'], $item['member']);
            }
            return $redisIns->exec();
        }

        // 不支持 pipeline 则循环插入
        foreach ($data as $item) {
            $this->addTask($item['runTime'], $item['member']);
        }
    }

    /**
     * @param $start
     * @param $end
     * @param array $options
     * @return mixed
     */
    public function popRangeExpires($start, $end, array $options = [])
    {
        return $this->redis->zRangeByScore($this->queue, $start, $end, $options);
    }
}